<template>
  <div>
    <h1>ref和reactive的区别</h1>
    <h2>关于reactive失去响应式的问题,下面两个demo均通过直接修改响应式数据本身的操作来验证。</h2>
    <h3>eg1:在挂载完成后,修改数据,希望下面界面内容从demo1变成hello world</h3>
    <h4>{{ demoObj.name }}</h4>
    <h3>eg2:挂载完成后,定时器3秒后,希望下面界面内容从demo2变成hello world</h3>
    <h4>{{ testObj.name }}</h4>
    <pre>
      let obj = reactive({})
      let demo = {name:'hello'}
      obj = demo
      总结:如果直接修改reactive声明的响应式数据obj本身,那么由于赋值操作,是复制的引用数据demo的指针,导致obj demo都指向了demo的地址,而demo是非响应式的,所以obj失去了响应式
    </pre>
    <h2>那么，用ref声明的引用类型，是否在直接赋值其他对象之后，失去响应式呢？</h2>
    <h3>eg1:在挂载完成后,修改数据,希望下面界面内容从demo1变成hello world</h3>
    <h4>{{ refObj.name }}</h4>
    <pre>
      let obj = ref({})
      let demo = {name:'hello world'}
      obj.value = demo
      总结：使用ref声明，直接赋值引用类型的数据，不会失去响应式。那么为什么会出现这种情况呢？
      我们看下core里ref的实现
      class RefImpl &lt;T&gt; {
        private _value: T
        private _rawValue: T
        // 依赖收集
        public dep?: Dep = undefined
        // ref标识
        public readonly __v_isRef = true

        constructor(value: T, public readonly __v_isShallow: boolean) {
          // isShallow:浅层ref不需要再次代理
          this._rawValue = __v_isShallow ? value : toRaw(value)
          // toReactive方法参考下面贴的内容，它首先判断了下value是不是对象，如果是对象，则直接走reactive的逻辑，否则直接返回value
          this._value = __v_isShallow ? value : toReactive(value)
        }

        get value() {
          // 收集
          trackRefValue(this)
          return this._value
        }

        set value(newVal) {
          const useDirectValue =
            this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
          newVal = useDirectValue ? newVal : toRaw(newVal)
          if (hasChanged(newVal, this._rawValue)) {
            this._rawValue = newVal
            // 在这里就可以看到，如果我们给ref声明的数据，重新赋值，且赋的值，在toReactive方法里判断是对象，则需要走一遍reactive逻辑，也就是重新代理了一遍。所以ref是不会丢失的
            this._value = useDirectValue ? newVal : toReactive(newVal)
            triggerRefValue(this, newVal)
          }
        }
      }

      这里把里面用到的关键的函数贴下
      export const toReactive = &lt;T extends unknown &gt;(value: T): T => isObject(value) ? reactive(value) : value

    </pre>
    所以关于reactive丢失代理的情况，稍微注意下即可。ref就是一个替代方案。
    那么只用reactive本身怎么解决呢？ 那么只能给数据多套一层，我们修改其中的属性即可。 eg:
    <pre>
      let list = reactive([])
      // 这样子的直接赋值，会导致数组失去响应式，表格数据没有刷新
      list = res.data
      // 外面再套一层，即可解决这种问题
      const obj = reactive({
        list:[]
      })
      obj.list = res.data
    </pre>
    <h2>关于解构，也会失去响应式的问题</h2>
  </div>
</template>
<script setup lang="ts">
import { handleReactive } from './reactive-lose'
import { handleRef } from './ref-lose'
const { demoObj, testObj } = handleReactive()
const refObj = handleRef()
</script>
<style lang="scss" scoped></style>
